package com.hulushuju.core.util;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * @Author: Hanjiafeng
 * @Date: 2019-05-30
 * @Desc: 类加载器
 */
public class ClassLoader {
    /**
     * 使用指定参数类型和参数绕过构造器权限直接实例化类
     * 当parameterTypes和parameters均为null时,通过默认构造器实例
     *
     * @param c              类
     * @param parameterTypes 参数类型
     * @param parameters     参数
     * @param <T>            类型
     * @return 实例化对象
     * @throws NoSuchMethodException     不存在参数类型对应的构造器
     * @throws InstantiationException    初始化失败
     * @throws IllegalAccessException    参数类型不对
     * @throws InvocationTargetException 调用异常
     */
    public static <T> T newInstance(Class<T> c, Class<?>[] parameterTypes, Object[] parameters) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {

        //参数类型和参数都为null
        boolean allNull = Objects.isNull(parameterTypes) && Objects.isNull(parameters);
        //参数类型和参数都不为null
        boolean noneNull = !Objects.isNull(parameterTypes) && !Objects.isNull(parameters);
        //参数和参数类型有一个为null
        boolean oneNull = !(allNull || noneNull);
        if (oneNull) {
            throw new IllegalArgumentException("参数类型个数和参数个数必须同时为null或者不为null");
        } else if (noneNull) {
            //检查参数类型和对应位置的参数的类型是否一致
            for (int i = 0; i < parameters.length; i++) {
                Class<?> typeClass = parameterTypes[i];
                Class<?> parameterClass = parameters[i].getClass();
                //检查parameters[i]是否由typeClass初始化
                boolean in = typeClass.isInstance(parameters[i]);
                //比较typeClass和parameterClass是否相同
                boolean eq = equalsClass(typeClass, parameterClass);
                if (!(in || eq)) {
                    throw new IllegalArgumentException("参数类型和对应位置参数的类型不相同");
                }
            }
        }
        //获取默认构造器
        Constructor<T> constructor = c.getDeclaredConstructor(parameterTypes);
        //开启无参构造器访问权限
        constructor.setAccessible(true);
        //实例化
        T t = constructor.newInstance(parameters);
        //关闭无参构造器访问权限
        constructor.setAccessible(false);
        return t;
    }

    /**
     * 比较c1和c2是否相同(基本类型与其对应封装类型视为相同)
     *
     * @param c1 c1
     * @param c2 c2
     * @return true/false
     */
    private static boolean equalsClass(Class<?> c1, Class<?> c2) {

        //比较类型是否相同
        if (c1 == c2) {
            return true;
        }

        //如果不相同,先判断是否是基本类型或者其分装类型
        //定义基本类型和其对应的封装类型
        List<Class<?>> primitiveClasses = Arrays.asList(new Class<?>[]{byte.class, short.class, int.class, long.class, float.class, double.class});
        List<Class<?>> classes = Arrays.asList(new Class<?>[]{Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class});
        //c1和c2都为基本类型或者封装类型
        boolean bb0 = primitiveClasses.contains(c1) || primitiveClasses.contains(c2) || classes.contains(c1) || classes.contains(c2);
        if (!bb0) {
            return false;
        }

        //比较基本类型或者封装类型c1和c2是否相同
        boolean b = false;
        for (int i = 0; i < classes.size(); i++) {
            Class<?> pC = primitiveClasses.get(i);
            Class<?> cC = classes.get(i);
            boolean bb1 = c1 == pC && c2 == cC;
            boolean bb2 = c1 == cC && c2 == pC;
            if (bb1 || bb2) {
                b = true;
                break;
            }
        }

        return b;
    }
}
